package LDraw.Support;
import java.util.ArrayList;
import java.util.HashMap;
import Color.ColorCategoryT;
import Command.LDrawColor;
import Command.LDrawColorT;
import LDraw.Files.LDrawFile;
public class ColorLibrary implements Cloneable {
private static HashMap<ColorCategoryT, ArrayList<LDrawColorT>> colorCategory;
private HashMap<LDrawColorT, LDrawColor> colors; // keys are
// LDrawColorT
// codes; objects
// are LDrawColors
private HashMap<LDrawColorT, LDrawColor> privateColors; // colors we
// might be
// asked to
// display,
// but
// should
// NOT be in
// the color
// picker
private static ColorLibrary sharedColorLibrary = null;
public ColorLibrary() {
colors = new HashMap<LDrawColorT, LDrawColor>();
privateColors = new HashMap<LDrawColorT, LDrawColor>();
}
// ---------- sharedColorLibrary
// --------------------------------------[static]--
//
// Purpose: Returns the global color library available to all LDraw objects.
// The colors are dynamically read from ldconfig.ldr.
//
// ------------------------------------------------------------------------------
public synchronized static ColorLibrary sharedColorLibrary() {
if (sharedColorLibrary == null) {
String ldconfigPath = null;
LDrawFile ldconfigFile = null;
// ---------- Read colors in ldconfig.ldr
// -------------------------------
// Read it in.
ldconfigPath = LDrawPaths.getInstance().ldconfigPath();
ldconfigFile = LDrawFile.fileFromContentsAtPath(ldconfigPath);
// "Draw" it so that all the colors are recorded in the library
HashMap<Integer, Boolean> optionMask = new HashMap<Integer, Boolean>();
optionMask.put(LDrawGlobalFlag.DRAW_NO_OPTIONS, true);
ldconfigFile.collectColor();
// [ldconfigFile draw: viewScale:1.0 parentColor:null];
sharedColorLibrary = ldconfigFile.activeModel().colorLibrary();
// ---------- Special Colors
// --------------------------------------------
// These meta-colors are chameleons that are interpreted based on
// the
// context. But we still need to create entries for them in the
// library
// so that they can be selected in the color palette.
LDrawColor currentColor = new LDrawColor();
LDrawColor edgeColor = new LDrawColor();
float currentColorRGBA[] = { 0.5f, 0.5f, 0.5f, 1.0f };
float edgeColorRGBA[] = { 0.5f, 0.5f, 0.5f, 1.0f };
// Make the "current color" a blah sort of beige. We display parts
// in
// the part browser using this "color"; that's the only time we'll
// ever
// see it.
currentColor.setColorCode(LDrawColorT.LDrawCurrentColor);
currentColor.setColorRGBA(currentColorRGBA);
// The edge color is never seen in models, but it still appears in
// the
// color panel, so we need to give it something.
edgeColor.setColorCode(LDrawColorT.LDrawEdgeColor);
edgeColor.setColorRGBA(edgeColorRGBA);
// Register both special colors in the library
sharedColorLibrary.addColor(currentColor);
sharedColorLibrary.addColor(edgeColor);
// ---------- Dithered Colors
// -------------------------------------------
// I'm only providing these to be a nice team player in the LDraw
// world.
LDrawColor blendedColor = null;
// Provide dithered colors for the entire valid range from LDRAW.EXE
for (LDrawColorT color : LDrawColorT.values()) {
if (color.getValue() < 256)
continue;
blendedColor = LDrawColor.blendedColorForCode(color);
if (blendedColor != null)
sharedColorLibrary.addPrivateColor(blendedColor);
}
// init color library
initColorCategory();
}
return sharedColorLibrary;
}
// ========== addPrivateColor:
// ==================================================
//
// Purpose: Adds the given color to the receiver, but doesn't make it
// visible to the color picker.
//
// Notes: This supports LDRAW.EXE's "dithered" colors, which sadly wormed
// their way into the part library, but should absolutely never be
// used in modeling. Keeping the "private" allows us to display
// old parts which may have been created with them without allowing
// them to otherwise pollute the user experience.
//
// ==============================================================================
private static void initColorCategory() {
colorCategory = new HashMap<ColorCategoryT, ArrayList<LDrawColorT>>();
for (ColorCategoryT category : ColorCategoryT.values())
colorCategory.put(category, new ArrayList<LDrawColorT>());
// Solid
for (LDrawColorT colorCode : LDrawColorT.values()) {
int colorValue = colorCode.getValue();
switch (colorValue) {
case 0:
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
case 11:
case 12:
case 13:
case 14:
case 15:
case 17:
case 18:
case 19:
case 20:
case 22:
case 23:
case 25:
case 26:
case 27:
case 28:
case 29:
case 30:
case 31:
case 68:
case 70:
case 71:
case 72:
case 73:
case 74:
case 77:
case 78:
case 84:
case 85:
case 86:
case 89:
case 92:
case 100:
case 110:
case 112:
case 115:
case 118:
case 120:
case 125:
case 151:
case 191:
case 212:
case 216:
case 226:
case 232:
case 272:
case 288:
case 308:
case 313:
case 320:
case 321:
case 322:
case 323:
case 326:
case 335:
case 351:
case 366:
case 373:
case 378:
case 379:
case 450:
case 462:
case 484:
case 503:
colorCategory.get(ColorCategoryT.Solid).add(colorCode);
break;
case 47:
case 40:
case 36:
case 38:
case 57:
case 54:
case 46:
case 42:
case 35:
case 34:
case 33:
case 41:
case 43:
case 39:
case 44:
case 52:
case 37:
case 45:
colorCategory.get(ColorCategoryT.Transparent).add(colorCode);
break;
case 334:
case 383:
case 60:
case 64:
case 61:
case 62:
case 63:
colorCategory.get(ColorCategoryT.Chrome).add(colorCode);
break;
case 80:
case 81:
case 82:
case 83:
case 87:
colorCategory.get(ColorCategoryT.Metallic).add(colorCode);
break;
case 75:
case 76:
case 132:
case 133:
colorCategory.get(ColorCategoryT.Speckle).add(colorCode);
break;
case 180:
case 150:
case 135:
case 179:
case 148:
case 137:
case 142:
case 297:
case 178:
case 134:
colorCategory.get(ColorCategoryT.Pearl).add(colorCode);
break;
case 114:
case 117:
case 129:
colorCategory.get(ColorCategoryT.Glitter).add(colorCode);
break;
case 79:
case 21:
case 294:
colorCategory.get(ColorCategoryT.Milky).add(colorCode);
break;
case 65:
case 66:
case 67:
case 256:
case 273:
case 324:
case 375:
case 406:
case 449:
case 490:
case 496:
case 504:
case 511:
colorCategory.get(ColorCategoryT.Rubber).add(colorCode);
break;
case 16:
case 24:
case 32:
case 493:
case 494:
case 495:
colorCategory.get(ColorCategoryT.CommonMaterial).add(colorCode);
break;
}
}
}
public ArrayList<LDrawColorT> getColorTList(ColorCategoryT category) {
return colorCategory.get(category);
}
public void addPrivateColor(LDrawColor newColor) {
LDrawColorT colorCode = newColor.colorCode();
privateColors.put(colorCode, newColor);
}
// ========== addColor:
// =========================================================
//
// Purpose: Adds the given color to the receiver.
//
// ==============================================================================
public void addColor(LDrawColor newColor) {
LDrawColorT colorCode = newColor.colorCode();
colors.put(colorCode, newColor);
}
// ========== colorForCode:
// =====================================================
//
// Purpose: Returns the LDrawColor object representing colorCode, or null if
// no such color number is registered. This method also searches
// the shared library, since its colors have global scope.
//
// ==============================================================================
public LDrawColor colorForCode(LDrawColorT colorCode) {
LDrawColor color = colors.get(colorCode);
// Try searching the private colors.
if (color == null) {
color = privateColors.get(colorCode);
}
// Try the shared library.
if (color == null && this != sharedColorLibrary) {
color = ColorLibrary.sharedColorLibrary().colorForCode(colorCode);
}
// Return something!
if (color == null) {
color = ColorLibrary.sharedColorLibrary
.colorForCode(LDrawColorT.LDrawCurrentColor);
}
return color;
}
// ========== complimentColorForCode:
// ===========================================
//
// Purpose: Returns the color that should be used when the compliment color
// is requested for the given code. Compliment colors are usually
// used to draw lines on the edges of parts.
//
// Notes: It may seem odd to have the method in the Color Library rather
// than the color object it. The reason is that a color may
// specify its compliment color either as actual color components
// or as another color code. Since colors have no actual knowledge
// of the library in which they are contained, we must look up the
// actual code here.
//
// Also note that the default ldconfig.ldr file defines most
// compliment colors as black, which is well and good for printed
// instructions, but less than stellar for onscreen display. The
// visual looks a lot more realistic when red has an edge color of,
// say, pink.
//
// ==============================================================================
public void getComplimentRGBA(float[] complimentRGBA, LDrawColorT colorCode) {
// TODO Auto-generated method stub
LDrawColor mainColor = colorForCode(colorCode);
LDrawColorT edgeColorCode = LDrawColorT.LDrawColorBogus;
if (mainColor != null) {
edgeColorCode = mainColor.edgeColorCode();
// If the color has a defined RGBA edge color, use it. Otherwise,
// look
// up the components of the color it points to.
if (edgeColorCode == LDrawColorT.LDrawColorBogus)
mainColor.getEdgeColorRGBA(complimentRGBA);
else
colorForCode(edgeColorCode).getColorRGBA(complimentRGBA);
}
}
// ========== complimentColor()
// =================================================
//
// Purpose: Changes the given RGBA color into a "complimentary" color, which
// stands out in the original color, but maintains the same hue.
//
// ==============================================================================
public void complimentColor(float[] originalColor, float[] complimentColor) {
float brightness = 0.0f;
// Isolate the color's grayscale intensity
// http://en.wikipedia.org/wiki/Grayscale
brightness = originalColor[0] * 0.30f + originalColor[1] * 0.59f
+ originalColor[2] * 0.11f;
// compliment dark colors with light ones and light colors with dark
// ones.
if (brightness > 0.3f) {
final float adjusingValue = brightness*0.1f;
// Darken
complimentColor[0] = Math.max(originalColor[0] - adjusingValue, 0.0f);
complimentColor[1] = Math.max(originalColor[1] - adjusingValue, 0.0f);
complimentColor[2] = Math.max(originalColor[2] - adjusingValue, 0.0f);
// for(int i=0; i < 3; i++)
// complimentColor[i] = originalColor[i];
} else {
final float adjusingValue = brightness*0.1f;
// Lighten
complimentColor[0] = Math.min(originalColor[0] + adjusingValue, 1.0f);
complimentColor[1] = Math.min(originalColor[1] + adjusingValue, 1.0f);
complimentColor[2] = Math.min(originalColor[2] + adjusingValue, 1.0f);
}
complimentColor[3] = originalColor[3];
}// end complimentColor
}